package com.zbkj.common.utils;

import com.aliyun.oss.*;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.Bucket;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;
import com.aliyun.oss.model.ListObjectsRequest;

import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.DeleteObjectsRequest;
import com.aliyun.oss.model.DeleteObjectsResult;
import com.aliyun.oss.model.CopyObjectResult;
import com.zbkj.common.exception.CrmebException;
import com.zbkj.common.vo.client.OssResponseVo;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.io.OutputStream;
import java.net.URL;

/**
 * OSS工具类
 * 
 * 使用示例：
 * 
 * 1. 下载文件为字节数组：
 * byte[] fileBytes = OssUtil.downloadFileAsBytes("client/1/123/material/file.jpg");
 * 
 * 2. 下载文件为输入流（推荐用于大文件）：
 * InputStream inputStream = OssUtil.downloadFileAsStream("client/1/123/material/file.jpg");
 * try {
 *     // 使用输入流处理文件
 *     // ...
 * } finally {
 *     inputStream.close(); // 必须关闭流
 * }
 * 
 * 3. 从完整URL下载文件：
 * byte[] fileBytes = OssUtil.downloadFileFromUrl("https://vision-smart.oss-cn-shanghai.aliyuncs.com/client/1/123/material/file.jpg");
 * 
 * 4. 从完整URL获取输入流：
 * InputStream inputStream = OssUtil.downloadFileFromUrlAsStream("https://vision-smart.oss-cn-shanghai.aliyuncs.com/client/1/123/material/file.jpg");
 * 
 * 5. 在Controller中流式下载文件：
 * @GetMapping("/download")
 * public void downloadFile(HttpServletResponse response) {
 *     try {
 *         InputStream inputStream = OssUtil.downloadFileAsStream("client/1/123/material/file.jpg");
 *         response.setContentType("application/octet-stream");
 *         response.setHeader("Content-Disposition", "attachment; filename=file.jpg");
 *         
 *         OutputStream outputStream = response.getOutputStream();
 *         byte[] buffer = new byte[1024];
 *         int bytesRead;
 *         while ((bytesRead = inputStream.read(buffer)) != -1) {
 *             outputStream.write(buffer, 0, bytesRead);
 *         }
 *         outputStream.flush();
 *         inputStream.close();
 *     } catch (Exception e) {
 *         // 处理异常
 *     }
 * }
 */
public class OssUtil {
    // 建议实际项目中通过配置中心或环境变量获取
    private static final String ENDPOINT = "oss-cn-shanghai.aliyuncs.com";
    private static final String OSS_ACCESS_KEY_ID = System.getenv("OSS_ACCESS_KEY_ID");
    private static final String OSS_ACCESS_KEY_SECRET = System.getenv("OSS_ACCESS_KEY_SECRET");
    private static final String BUCKET_NAME = System.getenv("OSS_BUCKET_NAME");

    public static OssResponseVo upload(MultipartFile file, Integer userId, String type, Integer merId) {
        String fileNameTemp = file.getOriginalFilename();
        String fileTypeSuffix = fileNameTemp.substring(fileNameTemp.lastIndexOf('.') + 1);
        String fileRealType = file.getContentType();
        // 生成新文件名：yyyyMMddHHmmss + userId + 随机1-1000 + .fileTypeSuffix
        String timeStr = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        int rand = new Random().nextInt(1000) + 1;
        String fileName = timeStr + userId + rand + "." + fileTypeSuffix;
        String ossPath = "client/" + merId + "/" + userId + "/" + type + "/" + fileName;
        OSS ossClient = null;
        try {
            ossClient = new OSSClientBuilder().build(ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
            // 检查bucket是否存在，不存在则创建
            if (!ossClient.doesBucketExist(BUCKET_NAME)) {
                ossClient.createBucket(new CreateBucketRequest(BUCKET_NAME));
            }
            // 上传文件
            InputStream inputStream = file.getInputStream();
            PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, ossPath, inputStream);
            ossClient.putObject(putObjectRequest);
            // 拼接url
            String url = "https://" + BUCKET_NAME + "." + ENDPOINT + "/" + ossPath;
            OssResponseVo vo = new OssResponseVo();
            vo.setUrl(url);
            vo.setFileTypeSuffix(fileTypeSuffix);
            vo.setFileRealType(fileRealType);
            return vo;
        } catch (IOException e) {
            throw new RuntimeException("OSS文件上传失败: " + e.getMessage(), e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 将网络流上传到OSS目标存储空间,例如将 http://www.baidu.com/1.png ->OSS空间
     * @param url
     * @param fileName
     * @param userId
     * @param merId
     * @param fileType
     * @param folderName
     * @param rootFolder 根目录
     * @return
     */
    public static String uploadFromUrlToOss(String url, String fileName, Integer userId, Integer merId, String fileType, String folderName, String rootFolder) {
        String objectName = String.format("%s/%d/%d/%s/%s", rootFolder, merId, userId, folderName, fileName);
        OSS ossClient =  new OSSClientBuilder().build(ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
        try {
            InputStream inputStream = new URL(url).openStream();
            PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, objectName, inputStream);
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            return String.format("https://%s.%s/%s", BUCKET_NAME, ENDPOINT.replace("https://", ""), objectName);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    public static String getFileType(String suffix) {
        String s = suffix.toLowerCase();
        if ("jpg,jpeg,png,gif,bmp,webp".contains(s)) return "图片";
        if ("mp3,wav,ogg".contains(s)) return "音频";
        if ("mp4,avi,rmvb,mov".contains(s)) return "视频";
        return "其他";
    }

    /**
     * 从OSS下载文件并返回字节数组
     * @param objectName OSS对象名称（文件路径）
     * @return 文件字节数组
     * @throws RuntimeException 当下载失败时抛出异常
     */
    public static byte[] downloadFileAsBytes(String objectName) {
        OSS ossClient = null;
        OSSObject ossObject = null;
        InputStream inputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        
        try {
            ossClient = new OSSClientBuilder().build(ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
            
            // 检查文件是否存在
            if (!ossClient.doesObjectExist(BUCKET_NAME, objectName)) {
                throw new RuntimeException("文件不存在: " + objectName);
            }
            
            // 获取文件对象
            ossObject = ossClient.getObject(BUCKET_NAME, objectName);
            inputStream = ossObject.getObjectContent();
            byteArrayOutputStream = new ByteArrayOutputStream();
            
            // 读取文件内容到字节数组
            byte[] readBuffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(readBuffer)) != -1) {
                byteArrayOutputStream.write(readBuffer, 0, bytesRead);
            }
            
            // 获取最终的字节数组
            return byteArrayOutputStream.toByteArray();
            
        } catch (OSSException oe) {
            throw new RuntimeException("OSS下载异常: " + oe.getErrorMessage(), oe);
        } catch (IOException e) {
            throw new RuntimeException("文件读取异常: " + e.getMessage(), e);
        } catch (Exception e) {
            throw new RuntimeException("下载文件失败: " + e.getMessage(), e);
        } finally {
            // 关闭资源
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
                if (ossObject != null) {
                    ossObject.close();
                }
            } catch (IOException e) {
                // 记录关闭资源时的异常，但不抛出
                System.err.println("关闭资源时发生异常: " + e.getMessage());
            }
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 从OSS下载文件并返回输入流
     * @param objectName OSS对象名称（文件路径）
     * @return 文件输入流
     * @throws RuntimeException 当下载失败时抛出异常
     */
    public static InputStream downloadFileAsStream(String objectName) {
        OSS ossClient = null;
        OSSObject ossObject = null;
        
        try {
            ossClient = new OSSClientBuilder().build(ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
            
            // 检查文件是否存在
            if (!ossClient.doesObjectExist(BUCKET_NAME, objectName)) {
                throw new RuntimeException("文件不存在: " + objectName);
            }
            
            // 获取文件对象并返回输入流
            ossObject = ossClient.getObject(BUCKET_NAME, objectName);
            return ossObject.getObjectContent();
            
        } catch (OSSException oe) {
            throw new RuntimeException("OSS下载异常: " + oe.getErrorMessage(), oe);
        } catch (Exception e) {
            throw new RuntimeException("下载文件失败: " + e.getMessage(), e);
        }
        // 注意：这里不关闭ossClient和ossObject，因为需要保持流打开状态
        // 调用方需要负责关闭返回的InputStream
    }

    /**
     * 从完整URL中提取OSS对象名称
     * @param fullUrl 完整的OSS URL
     * @return OSS对象名称
     */
    public static String extractObjectNameFromUrl(String fullUrl) {
        if (fullUrl == null || fullUrl.isEmpty()) {
            throw new IllegalArgumentException("URL不能为空");
        }
        
        // 移除协议和域名部分
        String urlWithoutProtocol = fullUrl.replace("https://", "").replace("http://", "");
        
        // 查找bucket名称的位置
        int bucketEndIndex = urlWithoutProtocol.indexOf(".");
        if (bucketEndIndex == -1) {
            throw new IllegalArgumentException("无效的OSS URL格式");
        }
        
        // 移除bucket名称和域名部分
        String objectPath = urlWithoutProtocol.substring(bucketEndIndex + 1);
        int domainEndIndex = objectPath.indexOf("/");
        if (domainEndIndex == -1) {
            throw new IllegalArgumentException("无效的OSS URL格式");
        }
        
        return objectPath.substring(domainEndIndex + 1);
    }

    /**
     * 从完整URL下载文件并返回字节数组
     * @param fullUrl 完整的OSS URL
     * @return 文件字节数组
     * @throws RuntimeException 当下载失败时抛出异常
     */
    public static byte[] downloadFileFromUrl(String fullUrl) {
        String objectName = extractObjectNameFromUrl(fullUrl);
        return downloadFileAsBytes(objectName);
    }

    /**
     * 从完整URL下载文件并返回输入流
     * @param fullUrl 完整的OSS URL
     * @return 文件输入流
     * @throws RuntimeException 当下载失败时抛出异常
     */
    public static InputStream downloadFileFromUrlAsStream(String fullUrl) {
        String objectName = extractObjectNameFromUrl(fullUrl);
        return downloadFileAsStream(objectName);
    }

    /**
     * 删除OSS中的单个文件
     * @param objectName OSS对象名称（文件路径）
     * @return 删除是否成功
     * @throws RuntimeException 当删除失败时抛出异常
     */
    public static boolean deleteFile(String objectName) {
        OSS ossClient = null;
        try {
            // 创建OSSClient实例
            ossClient = new OSSClientBuilder().build(ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
            
            // 检查文件是否存在
            if (!ossClient.doesObjectExist(BUCKET_NAME, objectName)) {
                throw new CrmebException("文件不存在: " + objectName);
            }
            
            // 删除文件
            ossClient.deleteObject(BUCKET_NAME, objectName);
            return true;
            
        } catch (OSSException oe) {
            throw new CrmebException("OSS删除异常: " + oe.getErrorMessage());
        } catch (Exception e) {
            throw new CrmebException("删除文件失败: " + e.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 从完整URL删除OSS文件
     * @param fullUrl 完整的OSS URL
     * @return 删除是否成功
     * @throws RuntimeException 当删除失败时抛出异常
     */
    public static boolean deleteFileFromUrl(String fullUrl) {
        String objectName = extractObjectNameFromUrl(fullUrl);
        return deleteFile(objectName);
    }

    /**
     * 批量删除OSS中的多个文件
     * @param objectNames OSS对象名称列表（文件路径列表）
     * @return 删除成功的文件列表
     * @throws RuntimeException 当删除失败时抛出异常
     */
    public static List<String> deleteMultiFile(List<String> objectNames) {
        if (objectNames == null || objectNames.isEmpty()) {
            throw new CrmebException("文件列表不能为空");
        }
        
        OSS ossClient = null;
        try {
            // 创建OSSClient实例
            ossClient = new OSSClientBuilder().build(ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
            
            // 批量删除文件
            DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(BUCKET_NAME)
                    .withKeys(objectNames)
                    .withEncodingType("url");

            DeleteObjectsResult deleteResult = ossClient.deleteObjects(deleteRequest);
            List<String> deletedObjects = deleteResult.getDeletedObjects();
            
            // 解码删除的文件名
            List<String> decodedDeletedObjects = new ArrayList<>();
            for (String obj : deletedObjects) {
                try {
                    String decodedObj = URLDecoder.decode(obj, "UTF-8");
                    decodedDeletedObjects.add(decodedObj);
                } catch (UnsupportedEncodingException e) {
                    // 如果解码失败，使用原始名称
                    decodedDeletedObjects.add(obj);
                }
            }
            
            return decodedDeletedObjects;
            
        } catch (OSSException oe) {
            throw new CrmebException("OSS批量删除异常: " + oe.getErrorMessage());
        } catch (Exception e) {
            throw new CrmebException("批量删除文件失败: " + e.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 批量删除OSS中的多个文件（从完整URL列表）,这里可以直接传完整的OSS路径
     * @param fullUrls 完整的OSS URL列表
     * @return 删除成功的文件列表
     * @throws RuntimeException 当删除失败时抛出异常
     */
    public static List<String> deleteMultiFileFromUrls(List<String> fullUrls) {
        if (fullUrls == null || fullUrls.isEmpty()) {
            throw new IllegalArgumentException("URL列表不能为空");
        }
        
        // 从URL中提取对象名称
        List<String> objectNames = new ArrayList<>();
        for (String fullUrl : fullUrls) {
            // 将 完整url去掉bucketName
            String objectName = extractObjectNameFromUrl(fullUrl);
            objectNames.add(objectName);
        }
        
        return deleteMultiFile(objectNames);
    }

    /**
     * 拷贝OSS文件到新地址
     * @param sourceUrl 已有OSS文件的完整URL
     * @param rootFolder 根目录
     * @param merId 商户ID
     * @param userId 用户ID
     * @param folderName 目标文件夹
     * @param fileName 目标文件名
     * @return 新文件的OSS完整URL
     */
    public static String copyOssFile(String sourceUrl, String rootFolder, Integer merId, Integer userId, String folderName, String fileName) {
        // 解析源objectName
        String sourceKey = extractObjectNameFromUrl(sourceUrl);
        String destinationKey = String.format("%s/%d/%d/%s/%s", rootFolder, merId, userId, folderName, fileName);
        OSS ossClient = null;
        try {
            ossClient = new OSSClientBuilder().build(ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
            CopyObjectResult result = ossClient.copyObject(BUCKET_NAME, sourceKey, BUCKET_NAME, destinationKey);
            return String.format("https://%s.%s/%s", BUCKET_NAME, ENDPOINT.replace("https://", ""), destinationKey);
        } catch (OSSException oe) {
            throw new RuntimeException("OSS拷贝异常: " + oe.getErrorMessage(), oe);
        } catch (Exception e) {
            throw new RuntimeException("OSS拷贝失败: " + e.getMessage(), e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 生成OSS URL
     * @param rootFolder 根目录
     * @param folderName 文件夹名称
     * @param userId 用户ID
     * @param merId 商户ID
     * @param fileName 文件名
     * @return OSS URL
     */
    public static String getOssUrl(String rootFolder, String folderName, Integer userId, Integer merId, String fileName) {
        return "https://" + BUCKET_NAME + "." + ENDPOINT + "/" + rootFolder + "/" + merId + "/" + userId + "/" + folderName + "/" + fileName;
    }

    /**
     * 统计某目录的文件总和大小
     * @param folderPath 目录路径，例如："client/1/123/material/"
     * @return 文件总大小（字节）
     * @throws RuntimeException 当统计失败时抛出异常
     */
    public static long calculateFolderSize(String folderPath) {
        if (folderPath == null || folderPath.trim().isEmpty()) {
            throw new IllegalArgumentException("目录路径不能为空");
        }

        // 确保路径以/结尾
        if (!folderPath.endsWith("/")) {
            folderPath = folderPath + "/";
        }

        OSS ossClient = null;
        try {
            ossClient = new OSSClientBuilder().build(ENDPOINT, OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET);
            
            long totalSize = 0;
            String nextMarker = null;
            ObjectListing objectListing;
            
            do {
                // 构建请求，指定前缀（目录路径）
                ListObjectsRequest listObjectsRequest = new ListObjectsRequest(BUCKET_NAME);
                listObjectsRequest.setPrefix(folderPath);
                listObjectsRequest.setMaxKeys(1000); // 每次最多返回1000个对象
                if (nextMarker != null) {
                    listObjectsRequest.setMarker(nextMarker);
                }
                
                // 列出对象
                objectListing = ossClient.listObjects(listObjectsRequest);
                
                // 计算文件大小总和
                for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
                    totalSize += objectSummary.getSize();
                }
                
                // 获取下一页的标记
                nextMarker = objectListing.getNextMarker();
                
            } while (objectListing.isTruncated()); // 如果还有更多对象，继续循环
            
            return totalSize;
            
        } catch (OSSException oe) {
            throw new RuntimeException("OSS统计目录大小异常: " + oe.getErrorMessage(), oe);
        } catch (Exception e) {
            throw new RuntimeException("统计目录大小失败: " + e.getMessage(), e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }



    /**
     * 格式化文件大小
     * @param bytes 字节数
     * @return 格式化后的文件大小字符串
     */
    public static String formatFileSize(long bytes) {
        if (bytes < 1024) {
            return bytes + " B";
        } else if (bytes < 1024 * 1024) {
            return String.format("%.2f KB", bytes / 1024.0);
        } else if (bytes < 1024 * 1024 * 1024) {
            return String.format("%.2f MB", bytes / (1024.0 * 1024));
        } else {
            return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
        }
    }

    /**
     * 统计某目录的文件总和大小并返回格式化结果
     * @param folderPath 目录路径，例如："client/1/123/material/"
     * @return 格式化后的文件总大小字符串
     * @throws RuntimeException 当统计失败时抛出异常
     */
    public static String calculateFolderSizeFormatted(String folderPath) {
        long size = calculateFolderSize(folderPath);
        return formatFileSize(size);
    }
} 